//
//  APIService.swift
//  Casecord
//
//  Created by baozi on 2024/1/3.
//

import Foundation
import Moya
import RxSwift
import CryptoSwift
import HandyJSON
import Alamofire
class APITargetType: TargetType {
    // 请求路径参数
    private var _route: APIRoute
    // 请求地址
    private var _baseURL: String
    // header
    private var _headers: [String: String]?
    // 参数编码
    private var _encoding: ParameterEncoding
    // 超时时间
    private var _timeout: TimeInterval
    // 是否属于三方api
    private var _thirdAPI: Bool
    // 是否加密
    private var _encrypt: Bool
    // 是否解密
    private var _decrypt: Bool
    // 是否签名
    private var _sign: Bool
    // 内容类型
    private var _contentType: String
    // 参数闭包
    private var _params: (() -> [String: Any?]?)?
    private var _paramters: [String: Any]? = nil
    private var _paramString: String?
    
    init(route: APIRoute,
         baseURL: String? = nil,
         headers: [String : String]? = nil,
         encoding: ParameterEncoding? = nil,
         timeout: TimeInterval = 20,
         thirdAPI: Bool = false,
         encrypt: Bool = true,
         decrypt: Bool = true,
         sign: Bool = true,
         contentType: String? = nil,
         params: (() -> [String: Any]?)? = nil)
    {
        self._route = route
        self._baseURL = Env.current.host
        self._headers = headers
        self._timeout = timeout
        self._thirdAPI = thirdAPI
        self._encrypt = encrypt
        self._decrypt = decrypt
        self._sign = sign
        
        self._params = params
        
        if let contentType {
            _contentType = contentType
        } else {
            if _route.method == .get || _route.method == .delete || _route.method == .head {
                self._contentType = "application/json"
            } else {
                self._contentType = "application/x-www-form-urlencoded"
            }
        }
     
        self._paramters = params?()
        // 添加前缀
        if let _paramters = self._paramters {
            var map: [String: Any] = [:]
            for kv in _paramters {
                let schemeKey = Env.current.httpScheme + kv.key
                map[schemeKey] = kv.value
            }
            self._paramters = map
        }
        if let encoding {
            self._encoding = encoding
        } else {
            if route.method == .get || route.method == .head || route.method == .delete {
                self._encoding = URLEncoding.default
                if let data = try? JSONSerialization.data(withJSONObject: _paramters ?? [:]), let jsonStr = String(data: data, encoding: .utf8) {
                    _paramString = jsonStr
                }
            } else if route.method == .post || route.method == .put {
                if let data = try? JSONSerialization.data(withJSONObject: _paramters ?? [:]), let jsonStr = String(data: data, encoding: .utf8) {
                    _paramString = jsonStr
                    if let aesString = CryptTool.aesEncrypt(value: jsonStr) {
                        let encoding = StringEncoding(string: aesString)
                        self._encoding = encoding
                    } else {
                        self._encoding = JSONEncoding.default
                    }
                } else {
                    self._encoding = JSONEncoding.default
                }
            } else {
                self._encoding = JSONEncoding.default
            }
        }
    }
    // MARK: -- Custom
    var timeout: TimeInterval {
        _timeout
    }
    
    var thirdApi: Bool {
        _thirdAPI
    }
    
    var decrypt: Bool {
        _decrypt
    }
    var encrypt: Bool {
        _encrypt
    }
    
    var sign: Bool {
        _sign
    }
    
    var paramtersString: String? {
        _paramString
    }
    
    var params: [String: Any]? {
        _paramters
    }
    // MARK: -- Moya
    var baseURL: URL {
        return URL(string: Env.current.host)!
    }
    
    var path: String {
        _route.path
    }
    
    var method: Moya.Method {
        _route.method
    }
    
    var task: Moya.Task {
        guard let _paramters else {
            return .requestPlain
        }
        return .requestParameters(parameters: _paramters, encoding: _encoding)
    }
    
    var headers: [String : String]? {
        _headers
    }
    
    var validationType: ValidationType {
        ValidationType.customCodes([200])
    }
    
}

extension APITargetType {
    func make() -> Single<APIResult> {
        apiProvider.make(self)
    }
}

extension PrimitiveSequenceType where Trait == SingleTrait {
    func async(disposeBag: DisposeBag? = nil) async throws -> Element {
        try await withCheckedThrowingContinuation { continuation in
            if let disposeBag = disposeBag {
                self.subscribe(onSuccess: { element in
                    continuation.resume(returning: element)
                }, onFailure: { error in
                    continuation.resume(throwing: error)
                })
                .disposed(by: disposeBag)
            } else {
                let _ = self.subscribe(onSuccess: { element in
                    continuation.resume(returning: element)
                }, onFailure: { error in
                    continuation.resume(throwing: error)
                })
            }
        }
    }
}

struct StringEncoding: ParameterEncoding {
    private let string:String?
    init(string: String?) {
        self.string = string
    }
    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.asURLRequest()
        let bodyData = string?.data(using: .utf8)
        urlRequest.httpBody = bodyData
        return urlRequest
    }
    
}
